/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2007  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

//#define	TRACE_LEVEL	2


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "ProcInfo.h"

// project's headers
#include "Malloc.h"
#include "SystInfo.h"
#include "Types.h"
#include "Trace.h"
#include "NtUndoc.h"


/******************************************************************/
/* Internal types                                                 */
/******************************************************************/

typedef NTSTATUS (*ENUMOBJECTCB)(POBJECT_HEADER,VOID*) ;

typedef struct
{
  PROCINFOOBJECTS * pStruct ;
  UINT		nStructSize ;
} STOREOBJECTINSTRUCTCONTEXT ;

/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

NTSTATUS _ProcInfo_EnumObjectsInHandleTable_WXP (PHANDLE_TABLE_WXP pHandleTable, ENUMOBJECTCB pfnCallback, PVOID pContext) ;

NTSTATUS _ProcInfo_StoreObjectInStruct (POBJECT_HEADER pObject, STOREOBJECTINSTRUCTCONTEXT* pContext) ;


/******************************************************************/
/* Internal function                                              */
/******************************************************************/

NTSTATUS _ProcInfo_GenericGetString (HANDLE		hProcess,
				     PUNICODE_STRING	pusString,				     
				     int		*pOffsets,
				     int		nOffsets)
{
  NTSTATUS			nStatus ;
  PROCESS_BASIC_INFORMATION	pbi ;
  int				iOffset ;

  // pointers in target process space
  INT_PTR		t_pCurrentBlock ;
  UNICODE_STRING	t_usString ;

  TRACE ;

  // assert paged memory is accessible
  PAGED_CODE() ;

  // verify params
  ASSERT (pusString!=NULL) ;
  ASSERT (pusString->Buffer!=NULL) ;

  nStatus = ZwQueryInformationProcess (hProcess,
				       ProcessBasicInformation,
				       &pbi, sizeof(pbi), NULL) ;
  
  if( nStatus != STATUS_SUCCESS )
    {
      TRACE_ERROR (TEXT("NtQueryInformationProcess failed (handle=0x%08X, status=0x%08X)\n"), hProcess, nStatus) ;
      return nStatus ;
    }

  TRACE_INFO (TEXT("PEB = 0x%08X\n"), pbi.PebBaseAddress);

  if( pbi.PebBaseAddress==NULL )
    {
      TRACE_INFO (TEXT("PEB of process 0x%08X is NULL\n"), hProcess) ;
      return STATUS_UNSUCCESSFUL ;
    }

  for( iOffset=0 ; iOffset<nOffsets-1 ; iOffset++ )
    {
      t_pCurrentBlock = (INT_PTR)pbi.PebBaseAddress ;

      TRACE_INFO (TEXT("Indirection %d : 0x%08X\n"), iOffset, t_pCurrentBlock+pOffsets[iOffset]);
      
      nStatus = ntundoc.ZwReadVirtualMemory (hProcess, 
					     (LPVOID)(t_pCurrentBlock+pOffsets[iOffset]),
					     &t_pCurrentBlock, sizeof(t_pCurrentBlock), NULL) ;
      
      if( nStatus != STATUS_SUCCESS )
	{
	  TRACE_ERROR (TEXT("ZwReadVirtualMemory failed (status=0x%08X)\n"), nStatus) ;
	  return nStatus ;
	}

      if( ! t_pCurrentBlock )
	{
	  TRACE_INFO (TEXT("Indirection %d (0x%02X bytes) leads to a NULL pointer\n"), 
		       iOffset, pOffsets[iOffset]) ;
	  return STATUS_UNSUCCESSFUL ;
	}
    }

  TRACE_INFO (TEXT("Reading UNICODE_STRING at 0x%08X\n"), t_pCurrentBlock+pOffsets[iOffset]);

  nStatus = ntundoc.ZwReadVirtualMemory (hProcess, 
					 (LPVOID)(t_pCurrentBlock+pOffsets[iOffset]),
					 &t_usString, sizeof(t_usString), NULL) ;

  if( nStatus != STATUS_SUCCESS )
    {
      TRACE_ERROR (TEXT("NtReadVirtualMemory failed (status=0x%08X)\n"), nStatus) ;
      return nStatus ;
    }  

  pusString->Length = min (pusString->MaximumLength, t_usString.Length) ;

  nStatus = ntundoc.ZwReadVirtualMemory (hProcess, t_usString.Buffer,
					 pusString->Buffer, pusString->Length, NULL) ;

  if( nStatus!=STATUS_SUCCESS )
    {
      TRACE_ERROR (TEXT("ZwReadVirtualMemory failed (status=0x%08X)\n"), nStatus) ;
      return nStatus ;
    }

  pusString->Buffer[pusString->Length/2] = 0 ;
  
  TRACE_INFO (TEXT("String = %ls\n"), pusString->Buffer) ;
  
  return nStatus ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetImagePath (HANDLE hProcess, PUNICODE_STRING pusPath) 
{
  NTSTATUS	nStatus ;
  int aOffsets[] = { 0x10, 0x38 } ;

  nStatus = _ProcInfo_GenericGetString (hProcess, pusPath, aOffsets, 2) ;

  if( nStatus!=STATUS_SUCCESS )
    TRACE_INFO (TEXT("_ProcInfo_GenericGetString failed (status=0x%08X)\n"), nStatus) ;

  return nStatus ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetSystemRoot (HANDLE hProcess, PUNICODE_STRING pusPath) 
{
  int aOffsets[] = { 0x54, 0x04, 0x00 } ;

  return _ProcInfo_GenericGetString (hProcess, pusPath, aOffsets, 3) ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetSystem32Root (HANDLE hProcess, PUNICODE_STRING pusPath) 
{
  int aOffsets[] = { 0x54, 0x04, 0x08 } ;

  return _ProcInfo_GenericGetString (hProcess, pusPath, aOffsets, 3) ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetCurDirDosPath (HANDLE hProcess, PUNICODE_STRING pusPath) 
{
  int aOffsets[] = { 0x10, 0x40 } ;

  return _ProcInfo_GenericGetString (hProcess, pusPath, aOffsets, 2) ;
}



/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetProcessId (HANDLE hProcess, PULONG pId) 
{
  NTSTATUS		nStatus ;
  PROCESS_BASIC_INFORMATION pbi ;
  ULONG			nReturnLen = 0 ;

  TRACE ;

  // assert paged memory is accessible
  PAGED_CODE() ;

  // verify params
  ASSERT (pId!=NULL) ;

  *pId = 0 ;

  nStatus = ZwQueryInformationProcess (hProcess,
				       ProcessBasicInformation,
				       &pbi, sizeof(pbi), &nReturnLen) ;

  if( nStatus!=STATUS_SUCCESS || nReturnLen<sizeof(pbi) )
    {
      TRACE_ERROR (TEXT("ZwQueryInformationProcess failed (handle=0x%08X,status=0x%08X)\n"), hProcess,nStatus) ;
      return nStatus ;
    }

  *pId = pbi.UniqueProcessId ;

  return STATUS_SUCCESS ;
}


NTSTATUS ProcInfo_GetAddress (HANDLE hProcess, PROCADDR * pnProcessAddress)
{
  LPVOID	pObject = NULL ;
  NTSTATUS	nStatus ;

  nStatus = ObReferenceObjectByHandle (hProcess, GENERIC_ALL,
				       NULL, KernelMode, &pObject, NULL) ;   

  if( nStatus!=STATUS_SUCCESS )
    {
      TRACE_ERROR (TEXT("ObReferenceObjectByHandle failed (status=0x%08X)\n"), nStatus) ;
      *pnProcessAddress = 0 ;
      return nStatus ;
    }

  ObDereferenceObject (pObject) ;

  *pnProcessAddress = (PROCADDR)pObject ;

  return STATUS_SUCCESS ;
}

/*
NTSTATUS ProcInfo_KillProcessFromPid (ULONG nProcessId) 
{
  HANDLE	hProcess ;
  OBJECT_ATTRIBUTES oa ;
  CLIENT_ID	clid ;
  NTSTATUS	nStatus ;

  InitializeObjectAttributes (&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL) ;
  
  clid.UniqueProcess = (HANDLE)nProcessId ;
  clid.UniqueThread = 0 ;
  
  nStatus = ntundoc.ZwOpenProcess (&hProcess, PROCESS_TERMINATE, &oa, &clid) ;
  
  if( nStatus != STATUS_SUCCESS )
    {
      TRACE_ERROR (TEXT("ZwOpenProcess failed (status=0x%08X)\n"), nStatus) ;
      return nStatus ;
    }

  nStatus = ntundoc.NtTerminateProcess (hProcess, 0) ;

  if( nStatus != STATUS_SUCCESS )
    TRACE_ERROR (TEXT("NtTerminateProcess failed (status=0x%08X)\n"), nStatus) ;

  ZwClose (hProcess) ;

  return nStatus ;
}
*/


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_GetCurrentProcessImagePath (PUNICODE_STRING pusString)
{
  BYTE *pEprocess, *pPeb ;
  RTL_USER_PROCESS_PARAMETERS *pUserProcParams ;

  pEprocess = (BYTE*) PsGetCurrentProcess() ;
  if( pEprocess == NULL ) return STATUS_UNSUCCESSFUL ;
  TRACE_BREAK (TEXT("EPROCESS = 0x%08X\n"), pEprocess) ;
  
  pPeb = *(BYTE**)( pEprocess + 0x01B0 ) ;
  if( pPeb == NULL ) return STATUS_UNSUCCESSFUL ;
  TRACE_INFO (TEXT("PEB = 0x%08X\n"), pPeb) ;

  pUserProcParams = *(RTL_USER_PROCESS_PARAMETERS**)( pPeb + 0x0010 ) ;
  if( pUserProcParams == NULL ) return STATUS_UNSUCCESSFUL ;
  TRACE_INFO (TEXT("RTL_USER_PROCESS_PARAMETERS = 0x%08X\n"), pUserProcParams) ;

  RtlCopyUnicodeString (pusString, &pUserProcParams->ImagePathName) ;
  
  TRACE_INFO (TEXT("String = %ls\n"), pusString->Buffer) ;
  
  return STATUS_SUCCESS ;
}


NTSTATUS _ProcInfo_ReadDwordAtPhysicalAddress (DWORD * pdwResult, INT_PTR nAddress)
{
  PHYSICAL_ADDRESS li = { .QuadPart = nAddress } ;
  PVOID ptr = MmMapIoSpace(li, 4, MmCached) ;

  if( ! ptr ) return STATUS_UNSUCCESSFUL ;

  *pdwResult = *(DWORD*)ptr ;
  
  MmUnmapIoSpace(ptr, 4) ;

  return STATUS_SUCCESS ;
}

/******************************************************************/
/* Internal function                                              */
/******************************************************************/

NTSTATUS _ProcInfo_VirtualToPhysical (DWORD nPageDirBase, DWORD nVirtalAddress, DWORD * pnPhysicalAddress)
{
  DWORD nPageDirIndex, nPageDirEntryAddr ;
  DWORD nPageDirEntryValue ;
  DWORD nPageTableIndex, nByteIndex ;
  DWORD nPageSize ;
  DWORD nPageTableBase, nPageTableEntryAddr, nPageTableEntryValue ;
  DWORD nPageBase ;

  TRACE_DWORD(nVirtalAddress) ;
  TRACE_DWORD(nPageDirBase) ;

  nPageDirIndex   = ( nVirtalAddress & 0xFFC00000 ) >> 22  ;
  nPageTableIndex = ( nVirtalAddress & 0x003FF000 ) >> 12  ;
  nByteIndex      = ( nVirtalAddress & 0x00000FFF ) ;
  TRACE_DWORD(nPageDirIndex) ;
  TRACE_DWORD(nPageTableIndex) ;
  TRACE_DWORD(nByteIndex) ;

  TRACE_DWORD(nPageDirIndex) ;
  
  nPageDirEntryAddr = nPageDirBase + nPageDirIndex * 4 ; 
  TRACE_DWORD(nPageDirEntryAddr) ;

  _ProcInfo_ReadDwordAtPhysicalAddress (&nPageDirEntryValue, nPageDirEntryAddr) ;
  TRACE_DWORD(nPageDirEntryValue) ;

  if( nPageDirEntryValue & 0x80 ) {
    TRACE_INFO ("It's in a 4MB page\n") ;
    nPageSize = 0x400000 ;
  }
  else {
    TRACE_INFO ("It's in a 4KB page\n") ;
    nPageSize = 0x1000 ;
  }

  if( nPageDirEntryValue & 0x01 ) {
    TRACE_INFO ("It's present in physical memory\n") ;
    }
  else {
    TRACE_INFO ("It's not present in physical memory\n") ;
    return STATUS_UNSUCCESSFUL ;
  }

  nPageTableBase = nPageDirEntryValue & 0xFFFFF000 ;
  TRACE_DWORD(nPageTableBase) ;

  nPageTableEntryAddr = nPageTableBase + nPageTableIndex * 4 ;
  TRACE_DWORD(nPageTableEntryAddr) ;

  _ProcInfo_ReadDwordAtPhysicalAddress (&nPageTableEntryValue, nPageTableEntryAddr) ;
  TRACE_DWORD(nPageTableEntryValue) ;

  if( nPageTableEntryValue & 0x01 ) {
    TRACE_INFO ("It's present in physical memory\n") ;
    }
  else {
    TRACE_INFO ("It's not present in physical memory\n") ;
    return STATUS_UNSUCCESSFUL ;
  }
  
  nPageBase = nPageTableEntryValue & 0xFFFFF000 ;
  TRACE_DWORD(nPageBase) ;

  *pnPhysicalAddress = nPageBase | nByteIndex ;
  TRACE_DWORD(*pnPhysicalAddress) ;

  return STATUS_SUCCESS ;
}

NTSTATUS _ProcInfo_ReadFromVirtualAddress (DWORD nPageDirBase, DWORD nVirtalAddress, VOID * pDestination, UINT nSize)
{
  NTSTATUS nStatus ;
  PHYSICAL_ADDRESS liPhysicalAddress ;
  PVOID pSource ;

  nStatus = _ProcInfo_VirtualToPhysical (nPageDirBase, nVirtalAddress, &liPhysicalAddress.LowPart) ;
  if( nStatus!=STATUS_SUCCESS ) return nStatus ;

  pSource = MmMapIoSpace(liPhysicalAddress, nSize, MmNonCached) ;
  if( ! pSource ) return STATUS_UNSUCCESSFUL ;

  RtlCopyMemory (pDestination, pSource, nSize) ;
  
  MmUnmapIoSpace(pSource, nSize) ;

  return STATUS_SUCCESS ;
}


#define COPY_PARAM_TO_INFO_STRUCT(param,field)				\
  {									\
    UINT nLength = min(sizeof(pInfo->field)-2,pParams->param.Length) ;	\
    if( _ProcInfo_ReadFromVirtualAddress (nPageDirBase, (DWORD)pParams->param.Buffer, pInfo->field, nLength) != STATUS_SUCCESS ) \
      nLength = 0 ;							\
    pInfo->field[nLength/2] = 0 ;					\
    TRACE_INFO(#field " = %ls\n", pInfo->field) ;			\
  }

/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_FillGeneralInfoStruct (PROCADDR pProc, PROCINFOGENERAL * pInfo) 
{
  NTSTATUS nStatus ;
  PEB peb ;
  PPEB pPeb ;
  RTL_USER_PROCESS_PARAMETERS params; 
  PRTL_USER_PROCESS_PARAMETERS pParams; 
  INT_PTR nPageDirBase ;

  ASSERT (pProc!=0) ;
  ASSERT (pInfo!=NULL) ;

  TRACE_INFO (TEXT("EPROCESS = 0x%08X\n"), pProc) ;

  switch( SystInfo_GetWindowsVersion() )
    {
    case 0x500:
    case 0x501:
      pPeb = *(PPEB*)( pProc + 0x01B0 ) ;
      break ;
    case 0x502:
      pPeb = *(PPEB*)( pProc + 0x0190 ) ;
      break ;
    case 0x600:
      pPeb = *(PPEB*)( pProc + 0x0188 ) ;
      break ;
    default:
      pPeb = NULL ;
    }

  if( pPeb == NULL ) return STATUS_UNSUCCESSFUL ;
  TRACE_INFO (TEXT("PEB = 0x%08X\n"), pPeb) ;

  nPageDirBase = *(DWORD*)( pProc + 0x0018 ) ;

  nStatus = _ProcInfo_ReadFromVirtualAddress (nPageDirBase, (INT_PTR)pPeb, &peb, sizeof(peb)) ;
  if( nStatus != STATUS_SUCCESS ) return nStatus ;

  pPeb = &peb ;

  pParams = pPeb->ProcessParameters ;
  if( pParams == NULL ) return STATUS_UNSUCCESSFUL ;
  TRACE_INFO (TEXT("RTL_USER_PROCESS_PARAMETERS = 0x%08X\n"), pParams) ;

  nStatus = _ProcInfo_ReadFromVirtualAddress (nPageDirBase, (INT_PTR)pParams, &params, sizeof(params)) ;
  if( nStatus != STATUS_SUCCESS ) return nStatus ;
  
  pParams = &params ;
  
  pInfo->nInfoType = 0 ;
  
  COPY_PARAM_TO_INFO_STRUCT(CurrentDirectoryName,szCurrentDirectory) ;
  COPY_PARAM_TO_INFO_STRUCT(DllPath,szDllPath) ;
  COPY_PARAM_TO_INFO_STRUCT(ImagePathName,szImagePathName) ;
  COPY_PARAM_TO_INFO_STRUCT(CommandLine,szCommandLine) ;
  COPY_PARAM_TO_INFO_STRUCT(WindowTitle,szWindowTitle) ;
  COPY_PARAM_TO_INFO_STRUCT(DesktopInfo,szDesktop) ;

  pInfo->nProcessAddress = (INT_PTR)pProc ;
  
  switch( SystInfo_GetWindowsVersion() )
    {
    case 0x0500:
    case 0x0600:
      pInfo->nProcessId = *(DWORD*)( pProc + 0x9C ) ;
      break ;
    case 0x0501:
    case 0x0502:
      pInfo->nProcessId = *(DWORD*)( pProc + 0x84 ) ;
      break;
    default:
      pInfo->nProcessId = 0 ;
    }    

  return STATUS_SUCCESS ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_FillThreadsInfoStruct (PROCADDR pProc, PROCINFOTHREADS * pStruct, UINT nStructSize) 
{
  NTSTATUS	nStatus ;
  INT_PTR	nPageDirBase ;
  LIST_ENTRY	*pThreadListHead ;
  LIST_ENTRY	*pCurrentListEntry ;

  ASSERT (pProc!=0) ;
  ASSERT (pStruct!=NULL) ;
  ASSERT (nStructSize>sizeof(PROCINFOTHREADS)) ;

  pStruct->nInfoType = 1 ;
  pStruct->nThreads = 0 ;

  TRACE_INFO (TEXT("EPROCESS = 0x%08X\n"), pProc) ;

  nPageDirBase = *(DWORD*)( pProc + 0x0018 ) ;
  TRACE_DWORD(nPageDirBase) ;

  switch( SystInfo_GetWindowsVersion() )
    {
    case 0x500:
    case 0x501:
    case 0x502:
    case 0x600:
      pThreadListHead = (LIST_ENTRY*)( pProc + 0x50 ) ;
      break ;
    default:
      TRACE_ERROR("Unsupported Windows version\n") ;
      return STATUS_UNSUCCESSFUL ;
    }

  TRACE_DWORD(pThreadListHead) ;

  if( ! pThreadListHead )
    {
      TRACE_ERROR (TEXT("ThreadListHead is NULL\n")) ;
      return STATUS_UNSUCCESSFUL ;
    }

  pCurrentListEntry = pThreadListHead ;

  while( pCurrentListEntry->Flink != pThreadListHead ) 
    {
      INT_PTR	vaTeb ;
      BYTE	*pKThread ;
      ULONGLONG	nKernelTime,nUserTime;
      TEB	teb ;

      if( nStructSize < sizeof(PROCINFOTHREADS) + (pStruct->nThreads+1) * sizeof(THREADINFO) )
	{
	  TRACE_ERROR("Buffer too small for %d threads\n",(pStruct->nThreads+1)) ;
	  return STATUS_BUFFER_TOO_SMALL ;
	}

      pCurrentListEntry = pCurrentListEntry->Flink ;
      TRACE_DWORD(pCurrentListEntry) ;

      switch( SystInfo_GetWindowsVersion() )
	{
	case 0x500:
	  pKThread = ((BYTE*)pCurrentListEntry) - 0x1a4 ;
	  break ;
	case 0x501:
	  pKThread = ((BYTE*)pCurrentListEntry) - 0x1b0 ;
	  break ;
	case 0x502:
	  pKThread = ((BYTE*)pCurrentListEntry) - 0x1ac ;
	  break ;
	case 0x600:
	  pKThread = ((BYTE*)pCurrentListEntry) - 0x1c4 ;
	  break ;
	default:
	  TRACE_ERROR("Unsupported Windows version\n") ;
	  return STATUS_UNSUCCESSFUL ;
	}
      
      TRACE_DWORD(pKThread) ;

      if( pKThread == NULL )
	{
	  TRACE_ERROR ("KTHREAD pointer is NULL\n") ;
	  return STATUS_UNSUCCESSFUL ;
	}

      if( pKThread[0] != 6 ) 
	{
	  TRACE_ERROR ("Pointer doesn't point to a KTHREAD object\n") ;
	  return STATUS_UNSUCCESSFUL ;
	}
      
      switch( SystInfo_GetWindowsVersion() )
	{
	case 0x500:
	  nKernelTime = *(DWORD*)( pKThread + 0x138 ) ;
	  nKernelTime *= 100000 ;
	  nUserTime = *(DWORD*)( pKThread + 0x13C ) ;
	  nUserTime *= 100000 ;
	  vaTeb = *(DWORD*)( pKThread + 0x20 ) ;
	  break ;
	case 0x501:
	  nKernelTime = *(DWORD*)( pKThread + 0x144 ) ;
	  nKernelTime *= 100000 ;
	  nUserTime = *(DWORD*)( pKThread + 0x148 ) ;	  
	  nUserTime *= 100000 ;
	  vaTeb = *(DWORD*)( pKThread + 0x20 ) ;
	  break ;
	case 0x502:
	  nKernelTime = *(DWORD*)( pKThread + 0x154 ) ;
	  nKernelTime *= 100000 ;
	  nUserTime = *(DWORD*)( pKThread + 0x158 ) ;	  
	  nUserTime *= 100000 ;
	  vaTeb = *(DWORD*)( pKThread + 0x30 ) ;
	  break ;
	case 0x600:
	  nKernelTime = *(DWORD*)( pKThread + 0x17C ) ;
	  nKernelTime *= 100000 ;
	  nUserTime = *(DWORD*)( pKThread + 0x1A8 ) ;	  
	  nUserTime *= 100000 ;
	  vaTeb = *(DWORD*)( pKThread + 0x84 ) ;
	  break ;
	default:
	  TRACE_ERROR("Unsupported Windows version\n") ;
	  return STATUS_UNSUCCESSFUL ;
	}
      
      TRACE_DWORD(vaTeb) ;

      nStatus = _ProcInfo_ReadFromVirtualAddress (nPageDirBase, vaTeb, &teb, sizeof(teb)) ;
      if( nStatus != STATUS_SUCCESS ) {
	TRACE_ERROR("_ProcInfo_ReadFromVirtualAddress failed\n") ;
	return nStatus ;
      }
      	  
      pStruct->aThreads[pStruct->nThreads].nKernelTime = nKernelTime ;
      pStruct->aThreads[pStruct->nThreads].nUserTime = nUserTime ;
      pStruct->aThreads[pStruct->nThreads].nThreadId = (DWORD)teb.ClientId.UniqueThread ;
      pStruct->nThreads++ ;	
    }

  TRACE_INFO ("Thread count = %u\n", pStruct->nThreads) ;

  return STATUS_SUCCESS ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS ProcInfo_FillObjectsInfoStruct (PROCADDR pProc, PROCINFOOBJECTS * pStruct, UINT nStructSize) 
{
  //  NTSTATUS	nStatus ;
  INT_PTR	nPageDirBase ;
  HANDLE_TABLE  *pHandleTable ;
  STOREOBJECTINSTRUCTCONTEXT context ;
  
  ASSERT (pProc!=0) ;
  ASSERT (pStruct!=NULL) ;
  ASSERT (nStructSize>sizeof(PROCINFOOBJECTS)) ;

  pStruct->nInfoType = 2 ;
  pStruct->nObjects = 0 ;

  context.pStruct = pStruct ;
  context.nStructSize = nStructSize ;

  TRACE_INFO (TEXT("EPROCESS = 0x%08X\n"), pProc) ;

  nPageDirBase = *(DWORD*)( pProc + 0x0018 ) ;
  TRACE_DWORD(nPageDirBase) ;

  switch( SystInfo_GetWindowsVersion() )
    {
    case 0x500:
      pHandleTable = *(HANDLE_TABLE**)( pProc + 0x128 ) ;
      break ;
    case 0x501:
      pHandleTable = *(HANDLE_TABLE**)( pProc + 0x0C4 ) ;
      break ;
    case 0x600:
      pHandleTable = *(HANDLE_TABLE**)( pProc + 0x0DC ) ;
      break ;
    default:
      TRACE_ERROR("Unsupported Windows version\n") ;
      return STATUS_UNSUCCESSFUL ;
    }

  TRACE_DWORD(pHandleTable) ;

  if( ! pHandleTable )
    {
      TRACE_ERROR (TEXT("ThreadListHead is NULL\n")) ;
      return STATUS_UNSUCCESSFUL ;
    }

  if( SystInfo_GetWindowsVersion() == 0x500 )
    {
      int iLayer1, iLayer2, iLayer3 ;   

      ExAcquireResourceExclusiveLite (&pHandleTable->w2k.HandleTableLock, TRUE) ;

      for( iLayer1=0 ; iLayer1<256 ; iLayer1++ )
	{
	  if( ! pHandleTable->w2k.Table[iLayer1]  ) continue ;

	  for( iLayer2=0 ; iLayer2<256 ; iLayer2++ )
	    {
	      if( ! pHandleTable->w2k.Table[iLayer1][iLayer2]  ) continue ;
	      
	      for( iLayer3=0 ; iLayer3<256 ; iLayer3++ )
		{
		  DWORD		nObjectBitsField ;
		  OBJECT_HEADER* pObjectHeader ;

		  nObjectBitsField = (DWORD)pHandleTable->w2k.Table[iLayer1][iLayer2][iLayer3].Object ;

		  if( ! nObjectBitsField ) continue ;

		  TRACE_DWORD(nObjectBitsField) ;
		  
		  pObjectHeader = (OBJECT_HEADER*)( (nObjectBitsField & 0xfffffff8) | 0x80000000 ) ;

		  _ProcInfo_StoreObjectInStruct (pObjectHeader, &context) ;
		}
	    }
	}

      ExReleaseResourceLite (&pHandleTable->w2k.HandleTableLock) ;
    }
  else if( SystInfo_GetWindowsVersion() == 0x501 )
    {
      int iLock ;
      
      for( iLock=0 ; iLock<4 ; iLock++ )
	ntundoc.ExfAcquirePushLockExclusive(&pHandleTable->wxp.HandleLock[iLock]) ;

      _ProcInfo_EnumObjectsInHandleTable_WXP (&pHandleTable->wxp,
					      (ENUMOBJECTCB)_ProcInfo_StoreObjectInStruct, 
					      &context) ;					     

      for( iLock=0 ; iLock<4 ; iLock++ )
	ntundoc.ExfReleasePushLock(&pHandleTable->wxp.HandleLock[iLock]) ;      
    }
  else    
    {
      _BREAK ;
      return STATUS_UNSUCCESSFUL ;
    }

  TRACE_INFO ("Object count = %u\n", pStruct->nObjects) ;

  return STATUS_SUCCESS ;
}

NTSTATUS _ProcInfo_StoreObjectInStruct (POBJECT_HEADER pObjectHeader, STOREOBJECTINSTRUCTCONTEXT* pContext) 
{
  UINT nStructSize = pContext->nStructSize ;
  PROCINFOOBJECTS * pStruct = pContext->pStruct ;

  if( nStructSize < sizeof(PROCINFOOBJECTS)+(pStruct->nObjects+1)*sizeof(OBJECTINFO) )
    {
      TRACE_ERROR ("Buffer too small for %d objects\n", pStruct->nObjects+1) ;
      return STATUS_BUFFER_TOO_SMALL ;
    }

  TRACE_DWORD(pObjectHeader) ;
  TRACE_DWORD(pObjectHeader->Type) ;
  
  if( pObjectHeader->Type )
    {
      UNICODE_STRING us ;
      
      us.Length = 0 ;
      us.MaximumLength = sizeof(pStruct->aObjects[pStruct->nObjects].szType) ;
      us.Buffer = pStruct->aObjects[pStruct->nObjects].szType ;
      
      RtlCopyUnicodeString (&us, &pObjectHeader->Type->Name) ;
    }
  else
    {
      pStruct->aObjects[pStruct->nObjects].szType[0] = 0 ;
    }
  
  TRACE_INFO ("Object type = %ls (key=0x%08X, index=0x%08X, code=0x%08X)\n", 
	      pStruct->aObjects[pStruct->nObjects].szType,
	      pObjectHeader->Type->Key, 
	      pObjectHeader->Type->Index,
	      pObjectHeader->Type->Code) ;	
  
  if( pObjectHeader->NameInfoOffset != 0 )
    {
      UNICODE_STRING us ;
      
      us.Length = 0 ;
      us.MaximumLength = sizeof(pStruct->aObjects[pStruct->nObjects].szName) ;
      us.Buffer = pStruct->aObjects[pStruct->nObjects].szName ;
      
      RtlCopyUnicodeString (&us, (UNICODE_STRING*)((BYTE*)pObjectHeader-pObjectHeader->NameInfoOffset+4)) ;
    }
  else
    {
      UNICODE_STRING us ;
      
      us.Length = 0 ;
      us.MaximumLength = sizeof(pStruct->aObjects[pStruct->nObjects].szName) ;
      us.Buffer = pStruct->aObjects[pStruct->nObjects].szName ;
      
      switch( pObjectHeader->Type->Key )
	{
	case OB_TYPE_TAG_FILE:			  			  
	  RtlCopyUnicodeString (&us, &((PFILE_OBJECT_W2K)pObjectHeader->Body)->FileName) ;
	  break ;

	case OB_TYPE_TAG_PROCESS:
	  
	  switch( SystInfo_GetWindowsVersion() )
	    {
	    case 0x501:
	      {
		PEPROCESS_WXP pEProcess = (PEPROCESS_WXP) pObjectHeader->Body ;
		ANSI_STRING as ;
		
		as.Buffer = pEProcess->ImageFileName ;
		as.Length = strlen(pEProcess->ImageFileName) ;
		as.MaximumLength = sizeof(pEProcess->ImageFileName) ;

		RtlAnsiStringToUnicodeString (&us, &as, FALSE) ;
	      }
	    }
	  
	  break ;
	  
	default:			  
	  
	  if( pObjectHeader->Type && pObjectHeader->Type->TypeInfo.QueryNameProcedure )
	    {
	      NTSTATUS nStatus ;
	      ULONG nReturnLength = 0 ;
	      ULONG nSize = 1024 ;
	      POBJECT_NAME_INFORMATION pBuffer = MALLOC(nSize) ; 
	      OB_QUERYNAME_METHOD pfn = (OB_QUERYNAME_METHOD)pObjectHeader->Type->TypeInfo.QueryNameProcedure ;
	      
	      nStatus = pfn (pObjectHeader->Body, 0, pBuffer, nSize, &nReturnLength);
	      
	      if( STATUS_SUCCESS == nStatus )
		{
		  TRACE_DWORD (nReturnLength) ;
		  
		  RtlCopyUnicodeString (&us, &pBuffer->Name) ;
		}
	      else
		{
		  TRACE_ERROR (TEXT("QueryNameProcedure failed (status=0x%08X)\n"), nStatus) ; 
		  pStruct->aObjects[pStruct->nObjects].szName[0] = 0 ; 
		}
	      
	      FREE (pBuffer) ;
	    }
	  else
	    pStruct->aObjects[pStruct->nObjects].szName[0] = 0 ;	
	  break ;
	}
      
    }
  
  TRACE_INFO ("Object name = %ls\n", pStruct->aObjects[pStruct->nObjects].szName) ;
  
  pStruct->nObjects++ ;
  TRACE_DWORD(pStruct->nObjects) ;		

  return STATUS_SUCCESS ;
}
  
NTSTATUS _ProcInfo_EnumObjectsInHandleTable_WXP (PHANDLE_TABLE_WXP pHandleTable, ENUMOBJECTCB pfnCallback, PVOID pContext)
{
  int nLevel ;

  // extract table hierarchy level from pointer
  nLevel = (int)pHandleTable & 3 ;

  // get the real pointer
  pHandleTable = (PHANDLE_TABLE_WXP)( (int)pHandleTable & ~3 ) ;

  switch( nLevel )
    {
    case 0:
      {
	int iEntry ;
	PHANDLE_TABLE_ENTRY pEntries = (PHANDLE_TABLE_ENTRY) pHandleTable->TableCode ;
	POBJECT_HEADER pObject ;
	
	for( iEntry=0 ; iEntry<512 ; iEntry++ )
	  {
	    pObject = (POBJECT_HEADER)( (DWORD)pEntries[iEntry].Object & ~3 ) ;
	    
	    if( pObject!=NULL )
	      pfnCallback (pObject, pContext) ;
	  }
      }
      break ;
      
    case 1:
    case 2:
      {
	PHANDLE_TABLE_WXP* pSubTables = (PHANDLE_TABLE_WXP*) pHandleTable->TableCode ;
	int iSubTable ;	
	int nSubTables = nLevel==2 ? 32 : 1024 ;

	for( iSubTable=0 ; iSubTable<nSubTables ; iSubTable++ )
	  _ProcInfo_EnumObjectsInHandleTable_WXP(pSubTables[iSubTable], pfnCallback, pContext) ;

      }
      break ;
    }
  
  return STATUS_SUCCESS ;
}
